
import movement

MOVE = "move"
BUY = "buy"
SELL = "sell"


class Merchant:
    """
    Class that represents a merchant in the world.  The base class is a merchant
    performs no actions on the do_turn method.
    """
    def __str__(self):
        titles = ["MONEY:", "LOCATION:", "GOODS:"]
        data = map(str, [self.money, self.location, self.goods])
        max_data = max(map(len, data))
        max_title = max(map(len, titles))
        
        string = ""
        
        for i in range(len(titles)):
            s = "{0:" + str(max_title) + "s}  {1:"+ str(max_data) +"s}\n"
            string = string + s.format(titles[i], data[i].rjust(max_data))
            
        return string
            
        
    
    def __init__(self, world, money = 1000):
        self.money = 1000
        self.world = world
        self.location = None
        self.goods = []
        
    def set_location(self, location):
        if self.location != None:
            t = self.world.get_town(self.location[0], self.location[1])
            t.merchants.remove(self)
        
        self.location = location
        t = self.world.get_town(location[0], location[1])
        
        t.merchants.add(self)
    
    def get_state(self):
        return _MerchantState(self.world,
                              self.location[0],
                              self.location[1],
                              self.goods)
    
    def do_turn(self):
        """
        Called when the Merchant is given the opportunity to take its action
        """
        #Do nothing

class PuppetMerchant(Merchant):
    """
    A Merchant that is controlled by an external force, whether it be user
    input or another module
    """
    
    def set_action(self, action):
        self.next_action = action
    
    def do_turn(self, rank):
        """
        Attempts to do the turn based on on the last instructions given
        """
        if self.next_action:
            self.next_action.resolve_action(self)
            self.money = self.money + self.next_action.get_reward()
        self.next_action = None
    
#*************************************
#****** ACTION ***********************
#*************************************

class _MerchantState:
    """
    Data construct containing all the necessary information for determining the
    state of the merchant.
    """
    def __init__(self, world, x, y, goods):
        """
        world - The world that the merchant is in
        x - The x coordinate of the merchant in this world
        y - The y coordinate of the merchant in this world
        goods - The goods that the merchant has (string[])
        """
        self.world = world
        self.x = x
        self.y = y
        self.goods = goods
        
    def __hash__(self):
        return hash(self._key())
        
    def __eq__(self, other):
        
        return self._key() == other._key()
    
    def _key(self):
        g = sum(map(hash, self.goods))
        return (id(self.world), self.x, self.y, g)
        
     

class _Action:
    """
    Class defining details of an action that can be taken
    """
    
    def __nonzero__(self):
        return self.is_valid()
    
    def __eq__(self, other):
        """
        Whether or not this action is equal to another action.
        """
        return self._key() == other._key()
        
    def __hash__(self):
        """
        The hash value for a action
        """
        return hash(self._key())
        
        
    def _key(self):
        """
        An identifying set of values for this action, abstract function must be
        overwritten.
        """
        raise ActionException("Abstract method called, " + str(self.__class__) +
                        " must override '_key' method.")
        
    
    def get_reward(self):
        """
        Return the reward generated from this action. This is a final method and
        should not be overwritten
        """
        if not self.is_valid():
            raise ActionException("Cannot get reward from invalid action")
        return self._get_reward();
        
      
    
    def _get_reward(self):
        """
        Return the reward.  This function is abstract so it must be overwritten
        for the individual action
        """
        raise ActionException("Abstract method called, " + str(self.__class__) +
                        " must override '_get_reward' method")
        
        
    def __nonzero__(self):
        return self.is_valid()
    
    def is_valid(self):
        """
        Return whether or not this Action is valid.  Overwrite this method.
        """
        raise ActionException("Abstract method called, " + str(self.__class__) +
                        " must override 'is_valid' method")
        
    def resolve_action(self, actor):
        """
        Resolves the action
        """
        
    
        
    
class _MoveAction(_Action):
    """
    An action for moving
    """
    def __init__(self, state, direction):
        """
        state - The state that the actor is in
        direction - The destination that the actor is moving to in the form
                      (x, y)
        """
        self.state = state
        self.direction = direction
        self.source = (state.x, state.y)
        self.destination = movement.translate(direction, self.source)
        self.route = movement.get_route(state.world, direction,
                                        (state.x, state.y))
            
    def is_valid(self):
        """Whether or not this action can be taken"""
        return self.route != None
    
    def _key(self):
        """
        See super definition.
        """
        return (MOVE, self.state, self.direction)
        
    
    def _get_reward(self):
        return -self.route.get_cost()
        
    def resolve_action(self, actor):
        actor.set_location(self.destination)


class _BuyAction(_Action):
    """
    An action for buying goods
    """
    def __init__(self, state, good):
        """
        state = The state that the actor is in
        good = The good to be bought
        """
        self.state = state
        self._valid = not state.goods
        self._town = state.world.get_town(state.x, state.y)
        self._good = good
        
    def _key(self):
        """
        See super definition.
        """
        return (BUY, self.state, self._good)
     
    def is_valid(self):
        """whether or not this buy can be performed"""
        return self._valid
    
    def get_reward(self):
        return -self._town.prices[self._good]
        
    def resolve_action(self, actor):
        actor.goods.append(self._good)
        
class _SellAction(_Action):
    """
    An action for selling goods
    """
    
    def __init__(self, state, good):
        """
        state = The state that the actor is in
        good = The good to be sold
        """
        self._valid = good in state.goods
        self._town = state.world.get_town(state.x, state.y)
        self._good = good
    
    def _key(self):
        """
        See super definition.
        """
        return (SELL, self.state, self._good)
    
    def is_valid(self):
        """whether or not this buy can be performed"""
        return self._valid
    
    def get_reward(self):
        return self._town.prices[self._good]
        
    def resolve_action(self, actor):
        actor.goods.remove(self._good)
        
def get_buy_action(actor, good):
    """Get a buy action for the given good for a given actor"""
    if not actor.location:
        raise ActionException(
            "Cannot create buy action for merchant with no location")
    state = actor.get_state()
    return _BuyAction(state, good)
    
def get_sell_action(actor, good):
    """Get a buy action for the given good for a given actor"""
    if not actor.location:
        raise ActionException(
            "Cannot create sell action for merchant with no location")
    state = actor.get_state()
    return _SellAction(state, good)

def get_move_action(actor, direction):
    """Get a move action for the given merchant in the given direction."""
    if not actor.location:
        raise ActionException(
            "Cannot create move action for merchant with no location")
    state = actor.get_state()
    return _MoveAction(state, direction)
    
        
class ActionException(Exception):
    """Wrapper class for exceptions created when actions are taken."""
    def __init__(self, message = ""):
        Exception.__init__(self, message)





        
    
        
        
        
    
        